package com.xjscrm.console.aop;

import com.xjscrm.common.annotation.MerId;
import com.xjscrm.console.biz.SysTenantService;
import com.xjscrm.console.config.datasource.DynamicDataSourceContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Objects;

/**
 * <p>
 * 数据源切换
 * </p>
 */
@Slf4j
@Aspect
@Component
@Order(-100)
public class DynamicDataSourceAop {
    @Autowired
    private SysTenantService sysTenantService;
    private static final String POINTCUT = "execution(* com.xjscrm.console.service..*.*(..))";
    private final static String MASTER_DATA_SOURCE_PACKAGE = "com.xjscrm.console.service.common";

    private final static String MER_ID = "merId";
    private final static String TENANT_CODE = "tenantCode";
    private final static String OBJECT_CLASS_NAME = "Object";



    @Around(POINTCUT)
    public Object doAddAround(ProceedingJoinPoint point) throws Throwable {
        DynamicDataSourceContextHolder.clearDataSourceKey();
        String name = point.getTarget().getClass().getName();
        if (name.startsWith(MASTER_DATA_SOURCE_PACKAGE)){
            // 设置默认的数据源
            DynamicDataSourceContextHolder.setDataSourceKey(DynamicDataSourceContextHolder.DEFAULT_DATASOURCE);
        }else {
            String tenantCode = getTenantCode(point);
            if (StringUtils.isNotBlank(tenantCode)){
                log.info("CUT_TENANT_DATA_SOURCE：{}", tenantCode);
                // 切换租户数据源
                DynamicDataSourceContextHolder.setDataSourceKey(tenantCode);
            }else{
                DynamicDataSourceContextHolder.setDataSourceKey(DynamicDataSourceContextHolder.DEFAULT_DATASOURCE);
            }
        }
        return point.proceed();
    }

    private String getTenantCode(ProceedingJoinPoint point) {

        Object[] objects = point.getArgs();
        Object object = objects[0];
        Class clazz = object.getClass();
        String tenantCode = null;
        Long merId = null;
        while (clazz != null && !clazz.getSimpleName().equals(OBJECT_CLASS_NAME)) {
            try {
                Field tenantField = clazz.getDeclaredField(TENANT_CODE);
                tenantField.setAccessible(true);
                tenantCode = (String)tenantField.get(object);
                break;
            } catch (Exception e) {
                log.error("err:{}", e.getMessage());
                clazz = clazz.getSuperclass();
            }
        }
        if (tenantCode == null){
            clazz = object.getClass();
            while (clazz != null && !clazz.getSimpleName().equals(OBJECT_CLASS_NAME)) {
                try {
                    Field merIdField = clazz.getDeclaredField(MER_ID);
                    merIdField.setAccessible(true);
                    merId = (Long)merIdField.get(object);
                    break;
                } catch (Exception e) {
                    log.error("err:{}", e.getMessage());
                    clazz = clazz.getSuperclass();
                }
            }

            // 添加单个参数处理
            if (Objects.isNull(merId)) {
                MethodSignature signature = (MethodSignature) point.getSignature();
                Method method = signature.getMethod();
                Parameter[] parameters = method.getParameters();
                for (int i = 0; i < parameters.length; i++) {
                    Parameter parameter = parameters[i];
                    MerId annotation = parameter.getAnnotation(MerId.class);
                    if (Objects.nonNull(annotation) && Long.class.isAssignableFrom(parameter.getType())) {
                        merId = (Long) objects[i];
                    }
                }
            }
        }
        if (StringUtils.isBlank(tenantCode) && merId!=null){
            tenantCode = sysTenantService.getTenantCodeByMerId(merId);
        }
        return tenantCode;
    }
}
